<?php
/**
 * Class rail_container
 *
 * A rail_container is a special rail_sequence. All objects in sequence has to be in containers inventory.
 *
 **
 * rail_object
 *   |
 *   + rail_sequence
 *       |
 *       + rail_container
 *
 * PHP versions 5
 * See http://www.open-rail.org for more informations.
 *
 **
 * Copyright (C)
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of ERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software
 * Fundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
 *
 **
 * @author Bastian Schröder <bs@chemanet.de>
 * @version 2007-01-23
 * @package rail
 */
class rail_container extends rail_sequence {

	/**
	 * Return a new rail_sequenceIterator object. Optional you can define the start index for the
	 * rail_sequenceIterator object.
	 *
	 * @exception rail_readpermission_exception
	 * @param int $pIndex (optional)
	 * @return rail_sequenceIterator
	 */
	public function get_iterator ( $pIndex ) {
		// check read permission
		if( !$this->is_readable() )
		 	throw new rail_readpermission_exception( $this, $this->rail_connector->get_user() );

		// remove all elements not who are not in this containers inventory
		$data = $this->rail_connector->send_request( "SELECT * FROM objects_sequences WHERE `sequence`='" . $this->get_id() . "'" );
		while( $data_row = $data->fetch_assoc() ){
			$object = rail_factory::get_object( $this->rail_connector, $data_row[ 'object' ] );
			if( $object->get_environment()->get_id() != $this->get_id() )
				$this->remove_element_force( $data_row[ 'index' ] );
		}

		// return iterator
		return parent::get_iterator( $pIndex );
	}


	/**
	 * Get all elements from sequence as array. The array keys are the index for the element.
	 *
	 * @exception rail_readpermission_exception
	 * @return array
	 */
	public function get_sequence () {
		// check read permission
		if( !$this->is_readable() )
		 	throw new rail_readpermission_exception( $this, $this->rail_connector->get_user() );

		// remove all elements not who are not in this containers inventory
		$elements = parent::get_sequence();
		foreach( $elements as $index=>$object )
			if( $object->get_environment()->get_id() != $this->get_id() ){
				$this->remove_element_force( $index );
				unset( $elements[ $index ] );
			}

		return $elements;
	}


	/**
	 * Insert a new rail_object as element to this sequence. The optional parameter pIndex specified a index in this
	 * sequence, the new element will be stored direct before the index.
	 * If the parameter $pIndex isn't set or null the new element will be placed at the beginning of this sequence.
	 *
	 * @exception rail_writepermission_exception
	 * @param rail_object $pRailObject
	 * @param int $pIndex (optional)
	 */
	public function insert_element ( $pRailObjects, $pIndex ) {
		// check write permission
		if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this, $this->rail_connector->get_user() );
		// check whether the rail_object is in this containers inventory
		if( $object->get_environment()->get_id() != $this->get_id() )
			throw new rail_exception( $this, $this->rail_connector->get_user(), 'The rail_object ' . $pRailObjects->get_id() . ' isn\'t in this containers inventory.' );

		parent::insert_element( $pRailObjects, $pIndex );
	}


	/**
	 * Returns true if this sequence is empty otherwise false.
	 *
	 * @exception rail_readpermission_exception
	 * @return boolean
	 */
	public function is_empty(){
		// check read permission
		if( !$this->is_readable() )
		 	throw new rail_readpermission_exception( $this, $this->rail_connector->get_user() );

		// remove all elements not who are not in this containers inventory
		$data = $this->rail_connector->send_request( "SELECT * FROM objects_sequences WHERE `sequence`='" . $this->get_id() . "'" );
		while( $data_row = $data->fetch_assoc() ){
			$object = rail_factory::get_object( $this->rail_connector, $data_row[ 'object' ] );
			if( $object->get_environment()->get_id() != $this->get_id() )
				$this->remove_element_force( $data_row[ 'index' ] );
		}

		// return parent is_empty
		return parent::is_empty();
	}


	/**
	 * Remove the element in top of this sequence. Return the removed rail_object.
	 *
	 * @exception rail_writepermission_exception
	 * @return rail_object
	 */
	public function pop () {
		// check write permission
		if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this, $this->rail_connector->get_user() );

		// pop elements until poping an element who is in this containers inventory
		do{
			$object = parent::pop();
		}
		while( $object->get_environment()->get_id() != $this->get_id() );

		// return element
		return $object;
	}


	/**
	 * Add a new element onto the top of this sequence.
	 *
	 * @exception rail_writepermission_exception
	 * @param array|rail_object $pRailObjects
	 */
	public function push ( $pRailObjects ) {
		// check write permission
		if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this, $this->rail_connector->get_user() );
		// check whether the rail_object(s) is in this containers inventory
		$objects = is_array($pRailObjects) ? $pRailObjects : array( $pRailObjects );
		foreach( $objects as $object )
			if( $object->get_environment()->get_id() != $this->get_id() )
				throw new rail_exception( $this, $this->rail_connector->get_user(), 'The rail_object ' . $object->get_id() . ' isn\'t in this containers inventory.' );

		parent::push( $pRailObjects );
	}


	/**
	 * Remove one element from this sequence. Require the index for the element how should be deleted.
	 * Do not check write permission.
	 *
	 * @param int $pId
	 */
	private function remove_element_force () {
		// check whether pIndex is a part of this sequence
		$data = $this->rail_connector->send_request( "SELECT * FROM objects_sequences WHERE `sequence`='" . $this->get_id() . "' AND `id`='$pIndex' LIMIT 1" );
		$element = $data->fetch_assoc();
		if( count( $element ) <= 0 )
			throw new rail_exception( $this, "The index $pIndex is not a part of this sequence.");

	 	// get element by index
		$data = $this->rail_connector->send_request( "SELECT * FROM objects_sequences WHERE `sequence`='" . $this->get_id() . "' AND `previous`='$pIndex' LIMIT 1" );
		// fetch element
		$element = $data->fetch_assoc();

		// get previous index for the new element
		$previous = count( $element ) > 0 ? $element[ 'id' ] : 0;

		// insert new element into sequence
		$id = $this->rail_connector->send_request( "
			INSERT INTO
				objects_sequences (`sequence`,`object`,`previous`,`next`)
			VALUES
				('" . $this->get_id() . "','" . $pRailObject->get_id() . "','$previous','$next')
		" );

		// update previous element in sequence
		if( $element[ 'previous' ] != 0 )
			$this->rail_connector->send_request( "UPDATE objects_sequences SET `next`='" . $element[ 'next' ] . "' WHERE `id`='" . $element[ 'previous' ] . "' LIMIT 1" );

		// update next element in sequence
		if( $element[ 'next' ] != 0 )
			$this->rail_connector->send_request( "UPDATE objects_sequences SET `previous`='" . $element[ 'previous' ] . "' WHERE `id`='" . $element[ 'next' ] . "' LIMIT 1" );

		// delete record
		$this->rail_connector->send_request( "DELETE FROM objects_sequences WHERE `id`='$pIndex'" );
	}


	/**
	 * Set an array of rail_objects as this sequence. Clear the sequence before adding new elements.
	 *
	 * @exception rail_writepermission_exception
	 * @param array $pRailObjects
	 */
	public function set_sequence ( $pRailObjects ) {
		// check write permission
		if( !$this->is_writeable() )
		 	throw new rail_writepermission_exception( $this, $this->rail_connector->get_user() );
		// check whether the rail_object(s) is in this containers inventory
		foreach( $pRailObjects as $object )
			if( $object->get_environment()->get_id() != $this->get_id() )
				throw new rail_exception( $this, $this->rail_connector->get_user(), 'The rail_object ' . $object->get_id() . ' isn\'t in this containers inventory.' );

		parent::set_sequence( $pRailObjects );
	}

} // End of rail_container
?>